/*
 * @Author: Tyler
 * @Date: 2025-10-15 15:30:18 
 * @Description: 
 * Copyright (c) 2025 by User/Corp, All Rights Reserved.
 */
#ifndef ACTIVATION_FUNCTION_H
#define ACTIVATION_FUNCTION_H

#include "Macros.h"
#ifdef USE_USER_DEFINED_TENSOR 
    #undef USE_EIGEN_TENSOR
    #include "Tensor.h"
#endif
#ifdef USE_EIGEN_TENSOR
    #undef USE_USER_DEFINED_TENSOR
    #include <Eigen/Core>
    #include <Eigen/Dense>
#endif
#include <iostream>
#include <memory>
#include <string>

#ifdef USE_EIGEN_TENSOR
// Enable Eigen parallelization
#define EIGEN_USE_THREADS
#endif

using namespace std;
#ifdef USE_EIGEN_TENSOR
using namespace Eigen;
#endif
#ifdef USE_USER_DEFINED_TENSOR
using namespace UserDefinedTensor;
#endif

// Activation Function Interface
#ifdef USE_USER_DEFINED_TENSOR
template <typename T, int Dynamic>
#endif
class ActivationFunction {
public:
    ActivationFunction() = default;
    virtual ~ActivationFunction() = default;
    virtual T activate(T x) const = 0;
    virtual T derivative(T x) const = 0;
    virtual Tensor<T, Dynamic>& activateToTensor(Tensor<T, Dynamic>& tensor) const {
        #ifdef USE_USER_DEFINED_TENSOR 
        if (tensor.getSize() == 0) {
        #endif
        #ifdef USE_EIGEN_TENSOR 
        if (tensor.size() == 0) {
        #endif
            throw runtime_error("Tensor is empty");
        }
        tensor = tensor.unaryExpr([this](T x) {
            return activate(x);
        });
        return tensor;
    }
    virtual Tensor<T, Dynamic>& derivativeToTensor(Tensor<T, Dynamic>& tensor) const {
        tensor = tensor.unaryExpr([this](T x) {
            return derivative(x);
        });
        return tensor;
    }
};

// Sigmoid Activation Function
#ifdef USE_USER_DEFINED_TENSOR 
template <typename T, int Dynamic>
#endif
class Sigmoid : public ActivationFunction<T, Dynamic> {
public:
    T activate(T x) const override;
    T derivative(T x) const override;
    Tensor<T, Dynamic>& activateToTensor(Tensor<T, Dynamic>& tensor) const override;
    Tensor<T, Dynamic>& derivativeToTensor(Tensor<T, Dynamic>& tensor) const override;
};

// ReLU Activation Function
#ifdef USE_USER_DEFINED_TENSOR 
template <typename T, int Dynamic>
#endif
class ReLU : public ActivationFunction<T, Dynamic> {
public:
    T activate(T x) const override;
    T derivative(T x) const override;
    Tensor<T, Dynamic>& activateToTensor(Tensor<T, Dynamic>& tensor) const override;
    Tensor<T, Dynamic>& derivativeToTensor(Tensor<T, Dynamic>& tensor) const override;
};

// Tanh Activation Function
#ifdef USE_USER_DEFINED_TENSOR 
template <typename T, int Dynamic>
#endif
class Tanh : public ActivationFunction<T, Dynamic> {
public:
    T activate(T x) const override;
    T derivative(T x) const override;
    Tensor<T, Dynamic>& activateToTensor(Tensor<T, Dynamic>& tensor) const override;
    Tensor<T, Dynamic>& derivativeToTensor(Tensor<T, Dynamic>& tensor) const override;
};

// Softmax Activation Function
#ifdef USE_USER_DEFINED_TENSOR
template <typename T, int Dynamic>
#endif
class Softmax : public ActivationFunction<T, Dynamic> {
public:
    T activate(T x) const override;
    T derivative(T x) const override;
    Tensor<T, Dynamic>& activateToTensor(Tensor<T, Dynamic>& tensor) const override;
    Tensor<T, Dynamic>& derivativeToTensor(Tensor<T, Dynamic>& tensor) const override;
};

// Activation Function Factory
#ifdef USE_USER_DEFINED_TENSOR
template <typename T, int Dynamic>
#endif
class ActivationFactory {
public:
    static unique_ptr<ActivationFunction<T, Dynamic> > create(const string& name);
    static const string& getName(const ActivationFunction<T, Dynamic>* func);
};

#endif // ACTIVATION_FUNCTION_H